package com.ubs.lingua.buildtool.cs.utils.ant;

import java.io.File;
import java.util.Collection;

import org.apache.ant.dotnet.compile.CSharp;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.logging.LogFactory;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Collections2;
import com.ubs.lingua.buildtool.cs.parameters.CscParameters;
import com.ubs.lingua.buildtool.cs.utils.RepositoryUtils;

/**
 * Wrapper for the optional Apache Ant CSC task.
 */
public final class CSharpCompiler extends CSharp {

    /**
     * Private constructor which setup the mock Ant project and adds the {@link AntLogListener} to it
     */
    private CSharpCompiler() {
        super();

        // creating mock project
        final Project prj = new Project();
        prj.setName("Mock CSharpCompiler project");
        prj.addBuildListener(new AntLogListener(LogFactory.getLog(CSharpCompiler.class)));
        setProject(prj);

        setTaskType("csc");
        setTaskName("csc");

        // creating mock target
        final Target target = new Target();
        target.setName("Mock CSC target");
        setOwningTarget(target);
    }

    /**
     * Compiles C# source into libraries.
     * csc.exe on Windows must be on the execute path.
     * @param srcDir the source directory of the files to be compiled
     * @param refDir the references directory of the DLLs to refer to
     * @param destDir the destination directory of files to be compiled
     * @param assemblyName the name of library to create
     * @param configuration debug-centric configurations of the C# compiler
     * @param generateXmlDoc whether XML documentation files should be generated
     * @param unsafe whether unsafe scope in a C# files is allowed
     * @param platform predefined target CPU configurations of the C# compiler
     * @see com.ubs.lingua.buildtool.cs.parameters.CscParameters.Configuration
     * @see com.ubs.lingua.buildtool.cs.parameters.CscParameters.Platform
     */
    public static void antCsc(final File srcDir, final File refDir, final File destDir, final String assemblyName,
                              final CscParameters.Configuration configuration, final boolean generateXmlDoc,
                              final boolean unsafe, final CscParameters.Platform platform) {
        final CSharpCompiler csc = new CSharpCompiler();
        csc.setDestFile(new File(destDir, assemblyName + ".dll"));
        csc.setSrcDir(srcDir);

        // Fixed options
        // 1. Fail build on error
        csc.setFailOnError(true);
        // 2. Do not print full paths in Log
        csc.setFullPaths(false);
        // 3. We always build DLLs
        final TargetTypes buildType = new TargetTypes();
        buildType.setValue("library");
        csc.setTargetType(buildType);
        // 4. WarnLevel - low
        csc.setWarnLevel(1);

        // Build configuration
        switch (configuration) {
        case DEBUG: {
            csc.setDebug(true);
            csc.setOptimize(false);
            break;
        }
        case RELEASE: {
            csc.setDebug(false);
            csc.setOptimize(true);
            break;
        }
        default: {
            // debug
            csc.setDebug(true);
            csc.setOptimize(false);
            break;
        }
        }

        // Documentation generation
        if (generateXmlDoc) {
            final File docFile = new File(destDir, RepositoryUtils.ASSEMBLY_DOC);
            csc.setDocFile(docFile);
        }

        // Target platform
        switch (platform) {
        case ANYCPU: {
            csc.setExtraOptions("/platform:anycpu");
            break;
        }
        case X64: {
            csc.setExtraOptions("/platform:x64");
            break;
        }
        case X86: {
            csc.setExtraOptions("/platform:x86");
            break;
        }
        default: {
            // any CPU
            csc.setExtraOptions("/platform:anycpu");
            break;
        }
        }

        // Other options
        csc.setUnsafe(unsafe);

        // References
        @SuppressWarnings("unchecked")
        final Collection<File> libs = FileUtils.listFiles(refDir, FileFilterUtils.suffixFileFilter(".dll"), null);
        final Collection<String> libPaths = Collections2.transform(libs, new Function<File, String>() {
            public String apply(final File file) {
                return file.getAbsolutePath();
            }
        });
        csc.setReferences(Joiner.on(';').join(libPaths));

        AntLauncher.launchTask(csc);
    }
}
